#include <iostream>
#include <stdint.h>
#include <vector>
#include "coroutineAsm.h"
#include "../logger/logger.h"


/**
    struct Registers {
    void *reg[8];
    };

    swap_context(void * old_register, void * new_register)
    new7    new6
    new5    new4
    new3    new2
    new1    new0
    old7    old6
    old5    old4
    old3    old2
    old1    old0

    mov 0x00(%rsp), %rdx   把rsp存储到rdx里面
    lea 0x08(%rsp), %rcx

*/
extern "C" void swap_context(void *, void *) asm("swap_context");
asm(R"(
swap_context:
  mov 0x00(%rsp), %rdx
  lea 0x08(%rsp), %rcx
  mov %r12, 0x00(%rdi)
  mov %r13, 0x08(%rdi)
  mov %r14, 0x10(%rdi)
  mov %r15, 0x18(%rdi)
  mov %rdx, 0x20(%rdi)
  mov %rcx, 0x28(%rdi)
  mov %rbx, 0x30(%rdi)
  mov %rbp, 0x38(%rdi)
  mov 0x00(%rsi), %r12
  mov 0x08(%rsi), %r13
  mov 0x10(%rsi), %r14
  mov 0x18(%rsi), %r15
  mov 0x20(%rsi), %rax
  mov 0x28(%rsi), %rcx
  mov 0x30(%rsi), %rbx
  mov 0x38(%rsi), %rbp
  mov %rcx, %rsp
  jmpq *%rax
)");




// 线程对应的主协程的生命周期归mainCoroutine管理
// 线程结束时, mainCoroutine变量销毁, 其管理的主协程堆内存也销毁
thread_local TinyRPC::CoroutineAsmPtr mainCoroutineAsm = nullptr;

// 线程对应的当前活跃协程， 但是其并不控制当前活跃协程的生命周期， 只是记录一下
thread_local TinyRPC::CoroutineAsmPtr currentCoroutineAsm = nullptr;




namespace TinyRPC{


CoroutineAsm::CoroutineAsm(CoroutineTask coroutineTask, int stackSize)
: m_stackSize(stackSize)
,  m_coroutineTask(coroutineTask){
    // 现需要为当前的主线程逻辑建立一个对于的协程。保存在mainCoroutineAsm， 方便后面切换
    GetMainCoroutine();
    m_mem.resize(stackSize);
    m_stack = (char *)((uintptr_t)(&m_mem.back()) & ~15ull) - sizeof(void *);
    m_context.reg[4] = (void *)CoroutineAsm::CoroutineFunc;
    m_context.reg[5] = m_stack;
    m_status = CoroutineStatus::INIT;
   
}

Registers& CoroutineAsm::getContext(){
    return m_context;
}
CoroutineAsm::CoroutineAsm(){
}

CoroutineAsm::~CoroutineAsm(){
    LOG_DEBUG<<"CoroutineAsm" <<m_name<<" del"<<std::endl;
    
}


// 进入协程
void CoroutineAsm::resume(){
    currentCoroutineAsm = this->shared_from_this();
    m_status = CoroutineStatus::RUNNING;
    swap_context(&mainCoroutineAsm->m_context, &m_context);
    currentCoroutineAsm = mainCoroutineAsm;
}

void CoroutineAsm::setStatus(CoroutineStatus status){
    m_status = status;
}

CoroutineStatus CoroutineAsm::getStatus(){
    return m_status;
}

//退出线程
void CoroutineAsm::Yield(){
    currentCoroutineAsm->m_status = STOP;
    swap_context(&currentCoroutineAsm->m_context, &mainCoroutineAsm->m_context);
}

// 这里并没有设置
void CoroutineAsm::Block(){
    currentCoroutineAsm->m_status = BLOCKED;
    swap_context(&currentCoroutineAsm->m_context, &mainCoroutineAsm->m_context);
}

void CoroutineAsm::setName(const std::string& name){
    m_name = name;
}
std::string CoroutineAsm::getName(){
    return m_name;
}



bool CoroutineAsm::InMain(){
return mainCoroutineAsm == nullptr ||
        mainCoroutineAsm == currentCoroutineAsm;
}


bool CoroutineAsm::InCoroutine(){
return currentCoroutineAsm != nullptr  &&
        currentCoroutineAsm != mainCoroutineAsm;
}


void CoroutineAsm::makeSureInMain(){
    if(mainCoroutineAsm == nullptr){
        GetMainCoroutine();
    }
    if(!CoroutineAsm::InMain()){
        LOG_ERROR<<"coroutine must call by main thread, donot support call by another coroutine";
        exit(1);
    }
}

void CoroutineAsm::GetMainCoroutine(){
    if(mainCoroutineAsm == nullptr){
        mainCoroutineAsm = CoroutineAsmPtr(new CoroutineAsm);
        currentCoroutineAsm = mainCoroutineAsm;
    }
}

std::shared_ptr<CoroutineAsm> CoroutineAsm::CurrentCoroutine(){
    return currentCoroutineAsm;
}


// 协程的执行入口， 里面执行用户的函数
void CoroutineAsm::CoroutineFunc(){
    // 这里使用捕捉异常，防止一个协程异常导致主线程崩溃
    try{
        currentCoroutineAsm->m_status = CoroutineStatus::RUNNING;
        currentCoroutineAsm->runTask();
    }catch(...){
        LOG_WARNING<<"coroutine run error";
    }
    // 跑完用户任务, 释放任务
    // 主动释放任务，防止出现循环引用
    // 成员m_coroutineTask 里面包含了coroutine自己， 这种情况是可能出现的  
    currentCoroutineAsm->m_coroutineTask = nullptr;
    currentCoroutineAsm->m_status = CoroutineStatus::END;
    // 切换到主线程
    // 不主动切换的话，会出现问题，程序无法执行
    // 因为协程的调用并不是程序的call， 而是jump，它执行完后完全不知道下一步怎么走。
    swap_context(&currentCoroutineAsm->getContext(), &mainCoroutineAsm->getContext());
    
}

}
